﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Luminal
{
    public enum AddressingMode : uint
    {
        None = 0x1130,
        ClampToEdge = 0x1131,
        Clamp = 0x1132,
        Repeat = 0x1133,
    }

    public enum FilterMode : uint
    {
        Nearest = 0x1140,
        Linear = 0x1141,
    }

    public sealed class Sampler : LuminalObject
    {
        #region Interop
        [DllImport("OpenCL.dll")]
        static extern IntPtr clCreateSampler(IntPtr context,
               uint normalized_coords,
               AddressingMode addressing_mode,
               FilterMode filter_mode,
               out ErrorCode errcode_ret);

        [DllImport("OpenCL.dll")]
        static extern ErrorCode clGetSamplerInfo(IntPtr sampler,
            cl_sampler_info param_name,
            SizeT param_value_size,
            IntPtr param_value,
            out SizeT param_value_size_ret);

        [DllImport("OpenCL.dll")]
        static extern ErrorCode clReleaseSampler(IntPtr sampler);
        #endregion

        #region Properties
        public Context Context { get; private set; }
        public bool NormalizedCoordinates { get; private set; }
        public AddressingMode AddressingMode { get; private set; }
        public FilterMode FilterMode { get; private set; }

        private T GetInfo<T>(cl_sampler_info info) where T : struct
        {
            SizeT infoSize = 0;
            Util.ThrowError("Could not get memory object property.", clGetSamplerInfo(InternalPointer, info, 0, IntPtr.Zero, out infoSize));

            T output = default(T);
            Util.Pin(ref output, ptr =>
            {
                Util.ThrowError("Could not get memory object property.", clGetSamplerInfo(InternalPointer, info, infoSize, ptr, out infoSize));
            });

            return output;
        }
        #endregion

        #region Constructor
        public Sampler(Context context, bool normalizedCoords, AddressingMode addressingMode, FilterMode filterMode)
        {
            ErrorCode error;
            Initialize(clCreateSampler(context.InternalPointer, normalizedCoords ? 1u : 0u, addressingMode, filterMode, out error));

            try
            {
                Util.ThrowError("Could not create sampler", error);
            }
            catch (Exception ex)
            {
                Dispose();
                throw ex;
            }

            Context = context;
            NormalizedCoordinates = normalizedCoords;
            AddressingMode = addressingMode;
            FilterMode = filterMode;
        }

        private Sampler(IntPtr ptr)
        {
            Initialize(ptr);

            try
            {
                Context = Context.FromPointer(GetInfo<IntPtr>(cl_sampler_info.CL_SAMPLER_CONTEXT));
                NormalizedCoordinates = GetInfo<bool>(cl_sampler_info.CL_SAMPLER_NORMALIZED_COORDS);
                AddressingMode = (AddressingMode)GetInfo<uint>(cl_sampler_info.CL_SAMPLER_ADDRESSING_MODE);
                FilterMode = (FilterMode)GetInfo<uint>(cl_sampler_info.CL_SAMPLER_FILTER_MODE);
            }
            catch (Exception ex)
            {
                Dispose();
                throw ex;
            }
        }
        #endregion

        protected override void Dispose(bool disposing)
        {
            if (disposing)
                Util.ThrowError("Could not release sampler.", clReleaseSampler(InternalPointer));

            base.Dispose(disposing);
        }

        public static Sampler FromPointer(IntPtr ptr)
        {
            return ObjectTracker.ContainsObject(ptr) ? ObjectTracker.GetObject<Sampler>(ptr) : new Sampler(ptr);
        }
    }
}
